package org.apache.james.hibernate.entity;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Transient;

import org.apache.james.mailboxmanager.Namespace;
import org.apache.james.mailboxmanager.Namespaces;

@Entity
public class ImapUserInfo {

	int id;
	String name;
	NamespaceImpl defaultNamespace;
	Set<NamespaceImpl> personalNameSpaces= new HashSet<NamespaceImpl>();
	Set<NamespaceImpl> sharedNameSpaces= new HashSet<NamespaceImpl>();
	Set<NamespaceImpl> userNameSpaces = new HashSet<NamespaceImpl>();
	
	Set<Group> groups = new HashSet<Group>();

	transient Namespaces namespaceInfo;
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@ManyToOne(fetch = FetchType.EAGER, cascade = {},optional = false)
	public NamespaceImpl getDefaultNamespace() {
		return defaultNamespace;
	}

	public void setDefaultNamespace(NamespaceImpl defaultNamespace) {
		this.defaultNamespace = defaultNamespace;
	}

	@ManyToMany(fetch = FetchType.EAGER, cascade = {})
	@JoinTable(name="ImapUserInfo_SharedNamespaces")
	public Set<NamespaceImpl> getSharedNameSpaces() {
		return sharedNameSpaces;
	}

	public void setSharedNameSpaces(Set<NamespaceImpl> sharedNameSpaces) {
		this.sharedNameSpaces = sharedNameSpaces;
	}

	@ManyToMany(fetch = FetchType.EAGER, cascade = {})
	@JoinTable(name="ImapUserInfo_UserNamespaces")
	public Set<NamespaceImpl> getUserNameSpaces() {
		return userNameSpaces;
	}

	public void setUserNameSpaces(Set<NamespaceImpl> userNameSpaces) {
		this.userNameSpaces = userNameSpaces;
	}

	@ManyToMany(fetch = FetchType.EAGER, cascade = {})
	@JoinTable(name="ImapUserInfo_UserGroups")
	public Set<Group> getGroups() {
		return groups;
	}
	
	public void setGroups(Set<Group> groups) {
		this.groups = groups;
	}
	
	@Transient
	public Namespaces getNamespaceInfo() {
		if (namespaceInfo==null) {
			namespaceInfo = new NamespacesInfo(this);
		}
		return namespaceInfo;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (obj == this) {
			return true;
		}
		if (obj instanceof ImapUserInfo) {
			return ((ImapUserInfo)obj).getId()==getId();
		}
		return super.equals(obj);
	}

	/**
	 * Return the namespace with the given name, or if it's not in the accessible namespace set of the user, then the default namespaces.
	 * @param nsName
	 * @return
	 */
	@Transient
	public NamespaceImpl getNamespace(String nsName) {
		for (NamespaceImpl sh : getSharedNameSpaces()) {
			if (nsName.equals(sh.getName())) {
				return sh;
			}
		}
		for (NamespaceImpl sh : getUserNameSpaces()) {
			if (nsName.equals(sh.getName())) {
				return sh;
			}
		}
		return getDefaultNamespace();
	}

	/**
	 * Return the namespace which this mailbox is belongs, it starts the searching in the personal namespaces, so it returns the
	 * 'mail.username' if 'mail.username.myfolder' is given, however 'mail.username' is just a child namespace of the 'mail' namespace.
	 * @param mailboxName
	 * @return the Namespace which the mailbox belongs.
	 */
	@Transient
	public Namespace getNamespaceFromMailbox(String mailboxName) {
		Namespace[] personal = getNamespaceInfo().getPersonal();
		Namespace n = getNamespaceFrom(mailboxName, personal);
		if (n==null) {
			n = getNamespaceFrom(mailboxName, getNamespaceInfo().getShared());
			if (n==null) {
				n = getNamespaceFrom(mailboxName, getNamespaceInfo().getUser());
			}
		}
		return n;
	}

	@Transient
	private Namespace getNamespaceFrom(String mailboxName, Namespace[] personal) {
		for (int i=0;i<personal.length;i++) {
			if (mailboxName.startsWith(personal[i].getName() + personal[i].getHierarchyDelimter())) {
				return personal[i];
			}
		}
		return null;
	}
}
